/* Copyright (C) 1997-2005, Antony L Hosking.
 * All rights reserved.  */
package Absyn;

import java.util.*;

import Parse.Token;
import Parse.MiniJavaConstants;

/**
 * Abstract Syntax Trees (ASTs)
 */
public abstract class MiniJava {
	private static void usage() {
		throw new Error("Usage: java MiniJava.Print <source>.java");
	}

	public static void main(String[] args) throws java.io.IOException {
		if (args.length != 1)
			usage();
		Parse.MiniJava parse = new Parse.MiniJava(new java.io.FileInputStream(args[0]));
		Unit unit;
		try {
			unit = parse.CompilationUnit();
		} catch (Parse.ParseException e) {
			System.err.println(e.getMessage());
			return;
		} catch (Parse.TokenMgrError e) {
			System.err.println(e.getMessage());
			return;
		}
		java.io.PrintWriter writer = new java.io.PrintWriter(System.out);
		unit.accept(new Print(writer), 0);
		writer.flush();
	}

	/**
	 * Each AST node is associated with a source program location, represented
	 * by a line number and a column number. This is inferred from the token
	 * used in constructing the AST node.
	 */
	protected final Token token;

	private MiniJava(Token t) {
		token = t;
	}

	/**
	 * @return line number
	 */
	public int line() {
		return token.beginLine;
	}

	/**
	 * @return column number
	 */
	public int column() {
		return token.beginColumn;
	}

	/**
	 * Each AST node accepts a Visitor
	 * 
	 * @param <D>
	 *                the domain of the visitor (i.e., argument type)
	 * @param <R>
	 *                the range of the visitor (i.e., result type)
	 * @param v
	 *                the visitor
	 * @param d
	 *                the visitor's argument
	 * @return the visitor's return value
	 */
	public abstract <D, R> R accept(Visitor<D, R> v, D d);

	public static abstract class Type extends MiniJava {
		private Type(Token t) {
			super(t);
		}

		public static class Primitive extends Type {
			public enum Kind {
				VOID, BOOLEAN, INT;
			}

			public Kind kind;

			/**
			 * A primitive type:
			 * 
			 * <pre>
			 * void | boolean | int
			 * </pre>
			 * 
			 * @param t
			 *                token
			 */
			public Primitive(Token t) {
				super(t);
				switch (t.kind) {
				case MiniJavaConstants.BOOLEAN:
					kind = Kind.BOOLEAN;
					break;
				case MiniJavaConstants.INT:
					kind = Kind.INT;
					break;
				case MiniJavaConstants.VOID:
					kind = Kind.VOID;
					break;
				}
			}

			public String toString() {
				return token.image;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Class extends Type {
			public final Name name;

			/**
			 * A class type
			 * 
			 * @param n
			 *                name
			 */
			public Class(Name n) {
				super(n.id);
				name = n;
			}

			public String toString() {
				return name.toString();
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Array extends Type {
			public final Type element;

			/**
			 * An array type
			 * 
			 * @param t
			 *                token
			 * @param e
			 *                element type
			 */
			public Array(Token t, Type e) {
				super(t);
				element = e;
			}

			public String toString() {
				return element + "[]";
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}
	}

	public static class Name extends MiniJava {
		public final Name prefix; // optional
		private final Token id;

		/**
		 * A name
		 * 
		 * @param p
		 *                prefix (may be null)
		 * @param i
		 *                identifier
		 */
		public Name(Name p, Token i) {
			super(i);
			prefix = p;
			id = i;
		}

		public String toString() {
			return ((prefix != null) ? prefix + "." : "") + id;
		}

		public <D, R> R accept(Visitor<D, R> v, D d) {
			return v.visit(this, d);
		}
	}

	public static class Unit extends MiniJava {
		/**
		 * A compilation unit
		 */
		public Unit() {
			super(null);
		}

		public final List<Dec.Class> types = new LinkedList<Dec.Class>();

		public <D, R> R accept(Visitor<D, R> v, D d) {
			return v.visit(this, d);
		}
	}

	public static class Modifier extends MiniJava {
		public enum Kind {
			STATIC, NATIVE, PUBLIC;
		}

		public final Kind kind;

		/**
		 * A modifier ("static", "native", "public")
		 * 
		 * @param t
		 *                modifier token
		 */
		public Modifier(Token t) {
			super(t);
			switch (t.kind) {
			case MiniJavaConstants.STATIC:
				kind = Kind.STATIC;
				break;
			case MiniJavaConstants.NATIVE:
				kind = Kind.NATIVE;
				break;
			case MiniJavaConstants.PUBLIC:
				kind = Kind.PUBLIC;
				break;
			default:
				throw new Error();
			}
		}

		public String toString() {
			return token.image;
		}

		public <D, R> R accept(Visitor<D, R> v, D d) {
			return v.visit(this, d);
		}
	}

	public static abstract class Dec extends MiniJava {
		public final List<Modifier> modifiers = new LinkedList<Modifier>();
		public final Token id;
		public final Type type;

		private Dec(Token id, Type t) {
			super(id);
			this.id = id;
			type = t;
		}

		public String toString() {
			return id.image;
		}

		public static class Class extends Dec {
			public final List<Dec> members = new LinkedList<Dec>();

			/**
			 * A class declaration: "class" id "extends" t "{" members "}"
			 * 
			 * @param id
			 *                class identifier
			 * @param t
			 *                parent type
			 */
			public Class(Token id, Type t) {
				super(id, t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Field extends Dec {
			/**
			 * A field declaration
			 * 
			 * @param id
			 *                field identifier
			 * @param t
			 *                field type
			 */
			public Field(Token id, Type t) {
				super(id, t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Method extends Dec {
			public final List<Dec.Formal> formals = new LinkedList<Dec.Formal>();
			public Stm.Block body = null; // optional (native)

			/**
			 * A method declaration
			 * 
			 * @param id
			 *                method identifier
			 * @param t
			 *                return type
			 */
			public Method(Token id, Type t) {
				super(id, t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Formal extends Dec {
			/**
			 * A formal parameter
			 * 
			 * @param id
			 *                formal identifier
			 * @param t
			 *                formal type
			 */
			public Formal(Token id, Type t) {
				super(id, t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Var extends Dec {
			public final Exp init; // optional

			/**
			 * A local variable
			 * 
			 * @param id
			 *                variable identifier
			 * @param t
			 *                variable type
			 * @param e
			 *                initializing expression (may be null)
			 */
			public Var(Token id, Type t, Exp e) {
				super(id, t);
				init = e;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}
	}

	public static class SwitchBlock extends MiniJava {
		public final List<Exp> expressions = new LinkedList<Exp>();
		public final ArrayList<List<Stm>> stmlists = new ArrayList<List<Stm>>();

		public int index_of_default = -1;
		public Exp switchExp;
		public int counter = 0;
		

		public SwitchBlock(Token id, Exp swExp) {
			super(id);
			switchExp = swExp;
		}

		public <D, R> R accept(Visitor<D, R> v, D d) {
			return v.visit(this, d);

		}
	}

	public static abstract class Stm extends MiniJava {
		private Stm(Token t) {
			super(t);
		}

		public static class Block extends Stm {
			public final List<Stm> stms = new LinkedList<Stm>();

			/**
			 * A block
			 * 
			 * @param t
			 *                token
			 */
			public Block(Token t) {
				super(t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Dec extends Stm {
			public final MiniJava.Dec dec;

			/**
			 * A local declaration
			 * 
			 * @param d
			 *                declaration
			 */
			public Dec(MiniJava.Dec d) {
				super(d.id);
				dec = d;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Exp extends Stm {
			public final MiniJava.Exp exp; // optional (empty statement)

			/**
			 * An expression statement: should be an assignment, a call, or a
			 * new instance expression
			 * 
			 * @param e
			 *                statement expression (may be null)
			 */
			public Exp(MiniJava.Exp e) {
				super(e.token);
				exp = e;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class If extends Stm {
			public final MiniJava.Exp test;
			public final Stm thenStm;
			public final Stm elseStm; // optional

			/**
			 * An if statement
			 * 
			 * @param t
			 *                token
			 * @param e
			 *                test expression
			 * @param s1
			 *                then statement
			 * @param s2
			 *                else statement (may be null)
			 */
			public If(Token t, MiniJava.Exp e, Stm s1, Stm s2) {
				super(t);
				test = e;
				thenStm = s1;
				elseStm = s2;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class While extends Stm {
			public final MiniJava.Exp test;
			public final Stm body;

			/**
			 * A while statement
			 * 
			 * @param t
			 *                token
			 * @param e
			 *                test expression
			 * @param s
			 *                body statement
			 */
			public While(Token t, MiniJava.Exp e, Stm s) {
				super(t);
				test = e;
				body = s;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class DoWhile extends Stm {
			public final MiniJava.Exp test;
			public final Stm body;

			/**
			 * A do while statement
			 * 
			 * @param t
			 *                token
			 * @param s
			 *                body statement
			 * @param e
			 *                test expression
			 */
			public DoWhile(Token t, Stm s, MiniJava.Exp e) {
				super(t);
				body = s;
				test = e;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Return extends Stm {
			public final MiniJava.Exp exp; // optional

			/**
			 * A return statement
			 * 
			 * @param t
			 *                token
			 * @param e
			 *                return value expression (may be null)
			 */
			public Return(Token t, MiniJava.Exp e) {
				super(t);
				exp = e;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Break extends Stm {
			/**
			 * A break statement
			 * 
			 * @param t
			 *                token
			 */
			public Break(Token t) {
				super(t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Continue extends Stm {
			/**
			 * A continue statement
			 * 
			 * @param t
			 *                token
			 */
			public Continue(Token t) {
				super(t);
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class For extends Stm {
			public final List<Stm> init; // optional
			public final MiniJava.Exp test; // optional
			public final List<Stm> update; // optional
			public final Stm body;

			/**
			 * A for statement
			 * 
			 * @param t
			 *                token
			 * @param i
			 *                list of expression and local variable declaration
			 *                statements
			 * @param e
			 *                test expression (may be null)
			 * @param u
			 *                list of statement expressions
			 * @param s
			 *                statement body
			 */
			public For(Token t, List<Stm> i, MiniJava.Exp e, List<Stm> u, Stm s) {
				super(t);
				init = i;
				test = e;
				update = u;
				body = s;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Switch extends Stm {
			public final MiniJava.Exp case_expression;
			public final SwitchBlock switch_block;

			/**
			 * A switch statement
			 * 
			 * @param t
			 *                token
			 * @param sb
			 *                switch block
			 * @param e
			 *                case expression
			 */
			public Switch(Token t, SwitchBlock sb, MiniJava.Exp e) {
				super(t);
				case_expression = e;
				switch_block = sb;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}
	}

	public static abstract class Exp extends MiniJava {
		/**
		 * Type inferred for expression by type checker
		 */
		public Semant.Type type;
		public int value; // valid for constant expressions only
		public boolean isConstant = false; // will be true for decimals and characters only

		public enum Operator {
			ASSIGN(1, "="),
			QUESTION(2, "?"),
			OR(3, "||"),
			AND(4, "&&"),
			BITOR(5, "|"),
			BITXOR(6, "^"),
			BITAND(7, "&"),
			EQ(8, "=="),
			NE(8, "!="),
			LT(9, "<"),
			GT(9, ">"),
			LE(9, "<="),
			GE(9, ">="),
			LSHIFT(10, "<<"),
			RSHIFT(10, ">>"),
			ARSHIFT(10, ">>>"),
			ADD(11, "+"),
			SUB(11, "-"),
			MUL(12, "*"),
			DIV(12, "/"),
			MOD(12, "%"),
			NEW(13, "new"),
			NOT(14, "!"),
			NEG(14, "-"),
			LPAREN(15, "("),
			LBRACK(15, "["),
			DOT(15, ".");

			public int precedence;
			private String string;

			Operator(int p, String s) {
				precedence = p;
				string = s;
			}

			public String toString() {
				return string;
			}

			static Operator map(Token t) {
				switch (t.kind) {
				case MiniJavaConstants.ASSIGN:
					return ASSIGN;
				case MiniJavaConstants.QUESTION:
					return QUESTION;
				case MiniJavaConstants.OR:
					return OR;
				case MiniJavaConstants.AND:
					return AND;
				case MiniJavaConstants.EQ:
					return EQ;
				case MiniJavaConstants.NE:
					return NE;
				case MiniJavaConstants.LT:
					return LT;
				case MiniJavaConstants.GT:
					return GT;
				case MiniJavaConstants.LE:
					return LE;
				case MiniJavaConstants.GE:
					return GE;
				case MiniJavaConstants.ADD:
					return ADD;
				case MiniJavaConstants.SUB:
					return SUB;
				case MiniJavaConstants.MUL:
					return MUL;
				case MiniJavaConstants.DIV:
					return DIV;
				case MiniJavaConstants.NEW:
					return NEW;
				case MiniJavaConstants.NOT:
					return NOT;
				case MiniJavaConstants.LPAREN:
					return LPAREN;
				case MiniJavaConstants.LBRACK:
					return LBRACK;
				case MiniJavaConstants.DOT:
					return DOT;
				default:
					throw new Error();
				}
			}
		}

		private Exp(Token t) {
			super(t);
		}

		public static class Assign extends Exp {
			public final Operator op;
			public final Exp left, right;

			/**
			 * An assignment expression
			 * 
			 * @param t
			 *                operator token "="
			 * @param l
			 *                left hand side: should be a variable (local, field
			 *                access, or array access)
			 * @param r
			 *                right hand side
			 */
			public Assign(Token t, Exp l, Exp r) {
				super(t);
				op = Operator.map(t);
				left = l;
				right = r;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class If extends Exp {
			public final Exp test;
			public final Exp thenExp;
			public final Exp elseExp;

			/**
			 * A conditional expression
			 * 
			 * @param t
			 *                operator token "?"
			 * @param e
			 *                test expression
			 * @param e1
			 *                then expression
			 * @param e2
			 *                else expression
			 */
			public If(Token t, Exp e, Exp e1, Exp e2) {
				super(t);
				test = e;
				thenExp = e1;
				elseExp = e2;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Infix extends Exp {
			public final Operator op;
			public final Exp left, right;

			/**
			 * A binary infix expression
			 * 
			 * @param t
			 *                operator token "+", "-", etc.
			 * @param l
			 *                left operand
			 * @param r
			 *                right operand
			 */
			public Infix(Token t, Exp l, Exp r) {
				super(t);
				op = Operator.map(t);
				left = l;
				right = r;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Prefix extends Exp {
			public Operator op;
			public final Exp exp;

			/**
			 * A prefix expression
			 * 
			 * @param t
			 *                operator token "!" or "-"
			 * @param e
			 *                operand
			 */
			public Prefix(Token t, Exp e) {
				super(t);
				op = (t.kind == MiniJavaConstants.SUB) ? Operator.NEG : Operator
						.map(t);
				exp = e;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Literal extends Exp {
			public enum Kind {
				TRUE, FALSE, STRING, CHAR, DEC, OCT, HEX, NULL;
			}

			public Kind kind;

			/**
			 * A literal expression
			 * 
			 * @param t
			 *                token
			 */
			public Literal(Token t) {
				super(t);
				switch (t.kind) {
				case MiniJavaConstants.TRUE:
					kind = Kind.TRUE;
					break;
				case MiniJavaConstants.FALSE:
					kind = Kind.FALSE;
					break;
				case MiniJavaConstants.STRING:
					kind = Kind.STRING;
					break;
				case MiniJavaConstants.CHAR:
					isConstant = true;
					value = Integer.parseInt(t.image);
					kind = Kind.CHAR;
					break;
				case MiniJavaConstants.DEC:
					isConstant = true;
					value = Integer.parseInt(t.image);
					kind = Kind.DEC;
					break;
				case MiniJavaConstants.OCT:
					isConstant = true;
					value = Integer.parseInt(t.image, 8);
					kind = Kind.OCT;
					break;
				case MiniJavaConstants.HEX:
					isConstant = true;
					value = Integer.parseInt(t.image, 16);
					kind = Kind.HEX;
					break;
				case MiniJavaConstants.NULL:
					kind = Kind.NULL;
					break;
				default:
					throw new Error();
				}
			}

			public String toString() {
				return token.image;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class This extends Exp {
			/**
			 * A this expression
			 * 
			 * @param t
			 *                token "this"
			 */
			public This(Token t) {
				super(t);
			}

			public String toString() {
				return token.image;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Super extends Exp {
			/**
			 * A super expression
			 * 
			 * @param t
			 *                token "super"
			 */
			public Super(Token t) {
				super(t);
			}

			public String toString() {
				return token.image;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Id extends Exp {
			/**
			 * An identifier expression
			 * 
			 * @param t
			 *                identifier token
			 */
			public Id(Token t) {
				super(t);
			}

			public String toString() {
				return token.image;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class NewInstance extends Exp {
			public final Type.Class classType;

			/**
			 * A new instance expression
			 * 
			 * @param t
			 *                operator token "new"
			 * @param c
			 *                class type
			 */
			public NewInstance(Token t, Type.Class c) {
				super(t);
				classType = c;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class NewArray extends Exp {
			public final Type element;
			public final List<Exp> dims = new LinkedList<Exp>();

			/**
			 * A new array expression
			 * 
			 * @param t
			 *                operator token "new"
			 * @param e
			 *                element type
			 */
			public NewArray(Token t, Type e) {
				super(t);
				element = e;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class FieldAccess extends Exp {
			public final Exp exp;
			public final Exp.Id selector;

			/**
			 * A field access expression
			 * 
			 * @param t
			 *                operator token "."
			 * @param e
			 *                operand
			 * @param s
			 *                field selector
			 */
			public FieldAccess(Token t, Exp e, Exp.Id s) {
				super(t);
				exp = e;
				selector = s;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class Call extends Exp {
			public final Exp exp;
			public final Exp.Id selector;
			public final List<Exp> actuals = new LinkedList<Exp>();

			/**
			 * A call expression
			 * 
			 * @param t
			 *                operator token "("
			 * @param e
			 *                operand
			 * @param s
			 *                method selector
			 */
			public Call(Token t, Exp e, Exp.Id s) {
				super(t);
				exp = e;
				selector = s;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}

		public static class ArrayAccess extends Exp {
			public final Exp exp, index;

			/**
			 * An array access expression
			 * 
			 * @param t
			 *                operator token "["
			 * @param e
			 *                operand
			 * @param i
			 *                index expression
			 */
			public ArrayAccess(Token t, Exp e, Exp i) {
				super(t);
				exp = e;
				index = i;
			}

			public <D, R> R accept(Visitor<D, R> v, D d) {
				return v.visit(this, d);
			}
		}
	}

	public static abstract class Visitor<Domain, Range> {
		public abstract Range visit(Unit u, Domain dom);

		public abstract Range visit(SwitchBlock switchBlock, Domain dom);

		public abstract Range visit(Name n, Domain dom);

		public abstract Range visit(Modifier m, Domain dom);

		public abstract Range visit(Dec.Class d, Domain dom);

		public abstract Range visit(Dec.Field d, Domain dom);

		public abstract Range visit(Dec.Method d, Domain dom);

		public abstract Range visit(Dec.Formal d, Domain dom);

		public abstract Range visit(Dec.Var d, Domain dom);

		public abstract Range visit(Type.Array n, Domain dom);

		public abstract Range visit(Type.Primitive n, Domain dom);

		public abstract Range visit(Type.Class n, Domain dom);

		public abstract Range visit(Stm.Block s, Domain dom);

		public abstract Range visit(Stm.Break s, Domain dom);

		public abstract Range visit(Stm.Continue s, Domain dom);

		public abstract Range visit(Stm.Dec s, Domain dom);

		public abstract Range visit(Stm.DoWhile s, Domain dom);

		public abstract Range visit(Stm.Exp s, Domain dom);

		public abstract Range visit(Stm.For s, Domain dom);

		public abstract Range visit(Stm.If s, Domain dom);

		public abstract Range visit(Stm.Return s, Domain dom);

		public abstract Range visit(Stm.While s, Domain dom);

		public abstract Range visit(Exp.ArrayAccess e, Domain dom);

		public abstract Range visit(Exp.Assign e, Domain dom);

		public abstract Range visit(Exp.Call e, Domain dom);

		public abstract Range visit(Exp.Id e, Domain dom);

		public abstract Range visit(Exp.If e, Domain dom);

		public abstract Range visit(Exp.FieldAccess e, Domain dom);

		public abstract Range visit(Exp.Infix e, Domain dom);

		public abstract Range visit(Exp.Literal e, Domain dom);

		public abstract Range visit(Exp.NewArray e, Domain dom);

		public abstract Range visit(Exp.NewInstance e, Domain dom);

		public abstract Range visit(Exp.Prefix e, Domain dom);

		public abstract Range visit(Exp.Super e, Domain dom);

		public abstract Range visit(Exp.This e, Domain dom);

		public abstract Range visit(Stm.Switch e, Domain dom);
	}

	/**
	 * A visitor to print the abstract syntax trees.
	 */
	public static class Print extends Visitor<Integer, Void> {
		public Print(java.io.PrintWriter o) {
			out = o;
		}

		java.io.PrintWriter out;
		private int i, c;
		private int ll = 65;

		public Void visit(Unit u, Integer p) {
			for (Dec d : u.types)
				d.accept(this, 0);
			return null;
		}

		public Void visit(Exp.Id e, Integer p) {
			say(e);
			return null;
		}

		public Void visit(Exp.Literal e, Integer p) {
			say(e);
			return null;
		}

		public Void visit(Exp.FieldAccess e, Integer p) {
			Exp.Operator op = Exp.Operator.DOT;
			int prec = op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			e.exp.accept(this, prec);
			say(op);
			e.selector.accept(this, prec);
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.ArrayAccess e, Integer p) {
			Exp.Operator op = Exp.Operator.LBRACK;
			int prec = op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			e.exp.accept(this, prec);
			say(op);
			int y = i++;
			e.index.accept(this, 0);
			i = y;
			say("]");
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.Infix e, Integer p) {
			int prec = e.op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			e.left.accept(this, prec);
			say(" " + e.op + " ");
			e.right.accept(this, prec);
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.If e, Integer p) {
			Exp.Operator op = Exp.Operator.QUESTION;
			int prec = op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			e.test.accept(this, prec);
			say(" " + op + " ");
			e.thenExp.accept(this, prec);
			say(" : ");
			e.elseExp.accept(this, prec);
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.NewArray e, Integer p) {
			Exp.Operator op = Exp.Operator.NEW;
			int prec = op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			say(op + " ");
			e.element.accept(this, 0);
			say("[");
			int y = i;
			i = c;
			print(e.dims, "][");
			i = y;
			say("]");
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.Call e, Integer p) {
			Exp.Operator op = Exp.Operator.LPAREN;
			int prec = op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			e.exp.accept(this, prec);
			say(".");
			e.selector.accept(this, prec);
			say(op);
			int y = i;
			i = c;
			print(e.actuals, ", ");
			i = y;
			say(")");
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.Super e, Integer p) {
			say(e);
			return null;
		}

		public Void visit(Exp.This e, Integer p) {
			say(e);
			return null;
		}

		public Void visit(Exp.Prefix e, Integer p) {
			int prec = e.op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			say(e.op);
			e.exp.accept(this, prec);
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.NewInstance e, Integer p) {
			Exp.Operator op = Exp.Operator.NEW;
			int prec = op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			say(op + " ");
			e.classType.accept(this, 0);
			say("()");
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Exp.Assign e, Integer p) {
			int prec = e.op.precedence;
			int x = i;
			if (prec <= p) {
				say("(");
				i += 1;
			}
			if (c >= ll) {
				out.println();
				indent(i);
			}
			e.left.accept(this, prec);
			say(" " + e.op + " ");
			e.right.accept(this, prec);
			if (prec <= p) {
				i = x;
				say(")");
			}
			return null;
		}

		public Void visit(Stm.Block s, Integer p) {
			indent(i);
			if (s.stms.isEmpty())
				say("{");
			else {
				sayln("{");
				i += 2;
				println(s.stms);
				i -= 2;
				indent(i);
			}
			sayln("}");
			return null;
		}

		public Void visit(Stm.Break s, Integer p) {
			indent(i);
			sayln("break;");
			return null;
		}

		public Void visit(Stm.Continue s, Integer p) {
			indent(i);
			sayln("continue;");
			return null;
		}

		public Void visit(Stm.DoWhile s, Integer p) {
			indent(i);
			sayln("do");
			i += 2;
			s.body.accept(this, 0);
			i -= 2;
			indent(i);
			say("while (");
			int x = i++;
			s.test.accept(this, 0);
			i = x;
			sayln(");");
			return null;
		}

		public Void visit(Stm.Exp s, Integer p) {
			indent(i);
			if (s.exp != null)
				s.exp.accept(this, 0);
			sayln(";");
			return null;
		}

		public Void visit(Modifier m, Integer p) {
			say(m);
			return null;
		}

		public Void visit(Stm.Dec s, Integer p) {
			indent(i);
			s.dec.accept(this, 0);
			sayln(";");
			return null;
		}

		public Void visit(Stm.If s, Integer p) {
			indent(i);
			say("if (");
			int x = i++;
			s.test.accept(this, 0);
			i = x;
			sayln(")");
			i += 2;
			s.thenStm.accept(this, 0);
			i -= 2;
			if (s.elseStm != null) {
				indent(i);
				sayln("else");
				i += 2;
				s.elseStm.accept(this, 0);
				i -= 2;
			}
			return null;
		}

		public Void visit(Stm.Return s, Integer p) {
			indent(i);
			say("return");
			if (s.exp != null) {
				say(" ");
				int x = i++;
				s.exp.accept(this, 0);
				i = x;
			}
			sayln(";");
			return null;
		}

		public Void visit(Stm.Switch s, Integer p) {
			indent(i);
			say("switch (");
			int x = i++;
			s.case_expression.accept(this, 0);
			i = x;
			sayln(")");	
			s.switch_block.switchExp = s.case_expression;
			s.switch_block.accept(this, 0);
			return null;
		}
		public Void visit(SwitchBlock sb, Integer p) {
			indent(i);
			if(sb.expressions.isEmpty()){
				say("{");
			}
			else{
				sayln("{");
				i+=2;
				int statement_index = 0;
				for (Exp exp : sb.expressions){
					indent(i);
					if (statement_index == sb.index_of_default)
						sayln("default:");
					
					else{
						say("case ");
						exp.accept(this, 0);
						sayln(":");
					}
					i+=4;
					List<Stm> expStatements = sb.stmlists.get(statement_index);
					println(expStatements);
					statement_index++;
					i-=4;
				}
			}
			i-=2;
			indent(i);
			sayln("}");

			return null;
		}

		public Void visit(Stm.While s, Integer p) {
			indent(i);
			say("while (");
			int x = i++;
			s.test.accept(this, 0);
			i = x;
			sayln(")");
			i += 2;
			s.body.accept(this, 0);
			i -= 2;
			return null;
		}
		public Void visit(Stm.For s, Integer p) {
			indent(i);
			say("for");
			i += 2;
			if (s.init != null) {
				sayln(" (");
				indent(i);
				boolean first = true;
				for (Stm e : s.init) {
					if (first)
						first = false;
					else
						say(", ");
					if (e instanceof Stm.Exp)
						((Stm.Exp) e).exp.accept(this, 0);
					else if (e instanceof Stm.Dec)
						((Stm.Dec) e).dec.accept(this, 0);
				}
			} else
				say(" (");
			if (s.test != null) {
				sayln(";");
				indent(i);
				s.test.accept(this, 0);
			} else
				say(";");
			if (s.update != null) {
				sayln(";");
				indent(i);
				boolean first = true;
				for (Stm e : s.update) {
					if (first)
						first = false;
					else
						say(", ");
					((Stm.Exp) e).exp.accept(this, 0);
				}
			} else
				say(";");
			sayln(")");
			s.body.accept(this, 0);
			i -= 2;
			return null;
		}

		public Void visit(Dec.Class d, Integer p) {
			indent(i);
			say("class " + d);
			if (d.type != null) {
				say(" extends " + d.type);
			}
			sayln(" {");
			i += 2;
			println(d.members);
			i -= 2;
			sayln("}");
			return null;
		}

		public Void visit(Dec.Field d, Integer p) {
			indent(i);
			for (Modifier m : d.modifiers) {
				m.accept(this, 0);
				say(" ");
			}
			d.type.accept(this, 0);
			sayln(" " + d + ";");
			return null;
		}

		public Void visit(Dec.Formal d, Integer p) {
			d.type.accept(this, 0);
			say(" " + d);
			return null;
		}

		public Void visit(Dec.Method d, Integer p) {
			indent(i);
			for (Modifier m : d.modifiers) {
				m.accept(this, 0);
				say(" ");
			}
			if (d.type == null)
				say("void");
			else
				d.type.accept(this, 0);
			say(" " + d + " (");
			int x = i++;
			print(d.formals, ", ");
			i = x;
			if (d.body == null)
				sayln(");");
			else if (d.body.stms.isEmpty())
				sayln(") {}");
			else {
				sayln(")");
				d.body.accept(this, 0);
			}
			return null;
		}

		public Void visit(Dec.Var d, Integer p) {
			d.type.accept(this, 0);
			say(" " + d);
			if (d.init != null) {
				say(" = ");
				int x = i++;
				d.init.accept(this, 1);
				i = x;
			}
			return null;
		}

		public Void visit(Name n, Integer p) {
			say(n);
			return null;
		}

		public Void visit(Type.Class t, Integer p) {
			t.name.accept(this, p);
			return null;
		}

		public Void visit(Type.Array t, Integer p) {
			t.element.accept(this, 0);
			say("[]");
			return null;
		}

		public Void visit(Type.Primitive t, Integer p) {
			say(t);
			return null;
		}

		private <E extends MiniJava> void println(List<E> l) {
			for (E e : l)
				e.accept(this, 0);
		}

		private <E extends MiniJava> void print(List<E> l, String sep) {
			boolean first = true;
			for (E e : l) {
				if (first) {
					if (e != null)
						e.accept(this, 0);
					first = false;
				} else {
					out.print(sep);
					c += sep.length();
					if (c + 1 >= ll) {
						out.println();
						indent(i);
					}
					if (e != null)
						e.accept(this, 0);
				}
			}
		}

		private void indent(int d) {
			for (int i = 0; i < d; i++)
				out.print(' ');
			c = d;
		}

		private void sayln(Object o) {
			say(o);
			out.println();
			c = 0;
			out.flush();
		}

		private void say(String s) {
			out.print(s);
			c += s.length();
		}

		private void say(Object o) {
			say(o.toString());
		}

	}
}
